﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using MockSchoolManagement.Core;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using MockSchoolManagement.Mvc.ViewModel;

namespace MockSchoolManagement.Mvc.Controllers
{
    [AllowAnonymous]
    public class AccountController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly ILogger<AccountController> _logger;

        public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, ILogger<AccountController> logger)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
        }

        [HttpGet]
        public IActionResult Register()
        {
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser()
                {
                    UserName = model.Email,
                    Email = model.Email,
                    City = model.City
                };
                
                //将用户数据存储在AspNetUser数据库表中
                var result = await _userManager.CreateAsync(user,model.Password);

                //如果成功创建用户，则使用登录服务登录用户信息
                //并重定向到HomeController的操作方法中
                if (result.Succeeded)
                {
                    //生成电子邮箱确认令牌
                    var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    //生成电子邮箱的确认链接
                    var confirmationLink = Url.Action("ConfirmEmail","Account",new {userId=user.Id,token=token},Request.Scheme);

                    //需要注入ILogger<AccountController> _logger服务，记录
                    _logger.Log(LogLevel.Warning, confirmationLink);

                    if (_signInManager.IsSignedIn(User)&&User.IsInRole("Admin"))
                    {
                        return RedirectToAction("ListUser", "Admin");
                    }

                    ViewBag.ErrorTitle = "注册成功";
                    ViewBag.ErrorMessage = $"在您登入系统前，我们已经给您发了一份邮件，需要您先进行邮件验证，单击确认链接即可完成";
                    return View("Error");
                    //await _signInManager.SignInAsync(user, isPersistent: false);
                    //return RedirectToAction("index", "Home");
                }

                //如果有任何错误，则将他们添加到ModelState对象中
                //将由验证摘要标记助手显示到视图中
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty,error.Description);
                }
            }

            return View(model);
        }
        [AcceptVerbs("Get","Post")]
        public async Task<IActionResult> IsEmailInUse(string email)
        {
            var user =await _userManager.FindByEmailAsync(email);
            if (user==null)
            {
                return Json(true);
            }

            return Json($"邮箱:{email}已经被注册使用了。");
        }

        [HttpGet]
        public async Task<IActionResult> Login(string returnUrl)
        {
            LoginViewModel model = new LoginViewModel()
            {
                ReturnUrl = returnUrl,
                ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
            };
            return View(model);
        }

        [HttpPost]
        public async Task<IActionResult> Login(LoginViewModel model,string returnUrl)
        {
            model.ReturnUrl = returnUrl;
            model.ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByEmailAsync(model.Email);
                if (user!=null&&!user.EmailConfirmed&&(await _userManager.CheckPasswordAsync(user,model.Password)))
                {
                    ModelState.AddModelError(string.Empty,"Email not confirmed yet");
                    return View(model);
                }
                //在PasswordSignInAsync()中我们将最后一个参数从false修改为True，用于启用账户锁定
                //每次登录失败后，都会将AspNetUsers表中的AccessFailedCount列值增加1，当他等于5时，
                //MaxFailedAccessAttempts将会锁定账户，然后修改LockoutEnd列，添加解锁时间，即使我们提供正确的用户名和密码，PasswordSignInAsync()
                //方法的返回值依然时Lockedout，即被锁定
                var result =
                    await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, true);
                if (result.Succeeded)
                {
                    if (!string.IsNullOrEmpty(returnUrl))
                    {
                        if (Url.IsLocalUrl(returnUrl))
                        {
                            return Redirect(returnUrl); 
                        }
                    }
                    return RedirectToAction("Index", "Home");
                }
                //如果账户状态为IsLockedout，那么我们重定向到AccountLocked视图，提示账户被锁定
                if (result.IsLockedOut)
                {
                    return View("AccountLocked");
                }

                ModelState.AddModelError(string.Empty,"登录失败，请重试。"); 

            }

            return View(model);
        }
        [HttpPost]
        public IActionResult ExternalLogin(string provider, string returnUrl)
        {
            var redirectUrl = Url.Action("ExternalLoginCallBack", "Account", new { ReturnUrl = returnUrl });
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
            return new ChallengeResult(provider, properties);
        }

        //[HttpPost]
        //public async void ExternalLogin(string provider, string returnUrl)
        //{
        //    await HttpContext.ChallengeAsync(MicrosoftAccountDefaults.AuthenticationScheme,
        //        new AuthenticationProperties() { RedirectUri = "/home/index" });
        //}


        public async Task<IActionResult> ExternalLoginCallBack(string returnUrl = null, string remoteError = null)
        {
            returnUrl ??= Url.Content("~/");

            LoginViewModel loginViewModel = new LoginViewModel()
            {
                ReturnUrl = returnUrl,
                ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
            };
            if (remoteError != null)
            {
                ModelState.AddModelError(string.Empty, $"第三方登录提供程序错误：{remoteError}");
                return View("Login", loginViewModel);
            }

            //从第三方登录提供商，即微软账户体系中，获取关于用户登录的信息
            
            var info = await _signInManager.GetExternalLoginInfoAsync();

            if (info == null)
            {
                ModelState.AddModelError(string.Empty, $"加载第三方登录信息出错。");
                return View("Login", loginViewModel);
            }

            var email = info.Principal.FindFirstValue(ClaimTypes.Email);
            ApplicationUser user = null;
            if (!string.IsNullOrEmpty(email))
            {
                //通过邮箱地址去查询用户是否已存在
                user = await _userManager.FindByEmailAsync(email);
                
                //如果电子邮箱没有被确认，返回登录视图与验证错误
                if (user!=null&&!user.EmailConfirmed)
                {
                    ModelState.AddModelError(string.Empty,"您的电子邮箱还未进行验证。");
                    return View("Login", loginViewModel);
                }
            }

            //如果用户之前已经登录过了，则会在AspNetUserLogins表有对应的记录，这个时候无需创建新的记录，直接使用当前记录登录系统即可
            var signinResult =
                await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false, true);
            if (signinResult.Succeeded)
                return LocalRedirect(returnUrl);
            //如果AspNetUserLogins表中没有记录，则表示用户没有一个本地账户，这个时候我们就需要创建一个记录了
            else
            {
                if (email != null)
                {
                    //通过邮箱地址去查询用户是否已存在
                     user = await _userManager.FindByEmailAsync(email);
                    if (user == null)
                    {
                        //如果不存在，则创建一个用户，但是这个用户没有密码
                        user = new ApplicationUser()
                        {
                            UserName = info.Principal.FindFirstValue(ClaimTypes.Email),
                            Email = info.Principal.FindFirstValue(ClaimTypes.Email)
                        };
                        await _userManager.CreateAsync(user);

                        //生成电子邮箱确认令牌
                        var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                        //生成电子邮箱的确认链接
                        var confirmationLint = Url.Action("ConfirmEmail", "Account",
                            new {userId = user.Id, token = token}, Request.Scheme);
                        
                        //记录生成的URL链接
                        _logger.LogWarning(confirmationLint);

                        ViewBag.ErrorTitle = "注册成功";
                        ViewBag.ErrorMessage = $"在您登入系统前，我们已经给您发了一封邮件，需要您先进行邮箱验证，单击确认链接即可完成";
                        return View("Error");
                    }

                    //在AspNetUserLogins表中添加一行用户数据，然后将当前用户登录到系统中
                    await _userManager.AddLoginAsync(user, info);
                    await _signInManager.SignInAsync(user, isPersistent: false);

                    return LocalRedirect(returnUrl);
                }

                //如果获取不到邮箱地址，则需要将请求重定向到错误视图中
                ViewBag.ErrorTitle = $"我们无法从提供商：{info.LoginProvider}中解析到读者的邮件地址";
                ViewBag.ErrorMessage = $"请通过联系abc@qq.com寻求技术支持。";
                return View("Error");
            }
        }
        [HttpGet]
        public async Task<IActionResult> ConfirmEmail(string userId,string token)
        {
            if (userId==null||token==null)
            {
                return RedirectToAction("Index", "Home");
            }

            var user = await _userManager.FindByIdAsync(userId);

            if (user==null)
            {
                ViewBag.ErrorMessage = $"当前{userId}无效";
                return View("NotFound");
            }

            var result = await _userManager.ConfirmEmailAsync(user, token);
            if (result.Succeeded)
            {
                return View();
            }

            ViewBag.ErrorTitle = "您的电子邮箱还未进行验证";
            return View("Error");
        }
        [HttpGet]
        public IActionResult ForgotPassword()
        {
            return View();
        }

        public async Task<IActionResult> ForgotPassword(EmailAddressViewModel  model)
        {
            if (ModelState.IsValid)
            {
                //通过邮箱地址查询用户地址
                var user = await _userManager.FindByEmailAsync(model.Email);

                //如果找到了用户并且确认了电子邮箱
                if (user!=null&&await  _userManager.IsEmailConfirmedAsync(user))
                {
                    //生成重置密码令牌
                    var token = await _userManager.GeneratePasswordResetTokenAsync(user);
                    //生成密码重置链接
                    var passwordResetLink = Url.Action("ResetPassword","Account",new {email=model.Email,token=token},Request.Scheme);
                    //将密码重置链接记录到文件中
                    _logger.LogWarning(passwordResetLink);

                    //重定向用户到忘记密码确认视图
                    return View("ForgotPasswordConfirmation");
                }
                //为了避免账户枚举和暴力攻击，不进行用户不存在或者邮箱未验证的提示
                return View("ForgotPasswordConfirmation");
            }

            return View(model);
        }
        [HttpGet]
        public IActionResult ResetPassword(string token,string email)
        {
            //如果密码重置令牌或电子邮箱为孔，则有可能是用户在视图篡改密码重置链接
            if (token==null||email==null)
            {
                ModelState.AddModelError(string.Empty,"无效的密码重置令牌");
              
            }
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                //通过邮箱地址查找用户
                var user = await _userManager.FindByEmailAsync(model.Email);
                if (user!=null)
                {
                    //重置用户密码
                    var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
                    if (result.Succeeded)
                    {
                        //密码重置成功后，如果当前账户被锁定，则设置该账户锁定结束时间为当前UTC日期时间，这样用户就可以用新密码登录系统
                        if (await _userManager.IsLockedOutAsync(user))
                        {
                            await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
                        }

                        return View("ResetPasswordConfirmation");
                    }
                    //显示验证错误信息，当密码重置令牌已用或密码复杂性规则不符合标准时，触发行为
                    foreach (var error in result.Errors)
                    {
                        ModelState.AddModelError(string.Empty,error.Description);
                    }

                    return View(model);
                }
                //为了避免账户枚举和暴力攻击，不要提示用户不存在
                return View("ResetPasswordConfirmation");
            }
            //如果模型验证未通过，则显示验证错误
            return View(model);
        }
        [HttpGet]
        public IActionResult ChangePassword()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.GetUserAsync(User);
                if (user==null)
                {
                    return RedirectToAction("Login");
                }
                //使用ChangePasswordAsync()方法更改用户密码
                var result = await _userManager.ChangePasswordAsync(user, model.CurrentPassword, model.NewPassword);
                //如果新密码不符合复杂性规则或者当前密码不正确，则需要将错误提示返回到ChangePassword视图中
                if (!result.Succeeded)
                {
                    foreach (var error in result.Errors)
                    {
                        ModelState.AddModelError(string.Empty,error.Description);
                    }

                    return View();
                }
                //更改密码成功，会刷新登录Cookie
                await _signInManager.RefreshSignInAsync(user);
                return View("ChangePasswordConfirmation");
            }

            return View(model);
        }

        [HttpPost]
        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return RedirectToAction("index","Home");
        }

    }
}
